#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <linux/limits.h>
#include <unistd.h>
#include <attr/xattr.h>
#include <errno.h>
#include "fscontroller.h"
#include "fslayer.h"

using namespace std;

class Logger : public FSLayer {
	public:
	Logger() {
		this->log = NULL;
	}
	~Logger() {
		if(this->log)
			this->close();
	}
	void open(const char *filename) {
		this->log = fopen(filename, "w");
		return;
	}
	void close() {
		fclose(log);
	}
	#define WRITE_LOG(...) fprintf(this->log, __VA_ARGS__); fflush(this->log);
	FUSE_GETATTR(const char *path, struct stat *statbuf) {
		WRITE_LOG("bb_getattr(path=\"%s\", statbuf=0x%08x)\n",
			path, (unsigned int) statbuf);
		/* passing control to the next layer */
		return FUSE_NEXT->fuse_getattr(path, statbuf, FUSE_NEXT_LAYER);
	}
	FUSE_READLINK(const char *path, char *link, size_t size) {
		WRITE_LOG("bb_readlink(path=\"%s\", link=\"%s\", size=%d)\n",
			path, link, size);
		return FUSE_NEXT->fuse_readlink(path, link, size);
	}
	FUSE_MKNOD(const char *path, mode_t mode, dev_t dev) {
		WRITE_LOG("bb_mknod(path=\"%s\", mode=0%3o, dev=%lld)\n",
			path, mode, dev);
		return FUSE_NEXT->fuse_mknod(  path, mode, dev);
	}
	FUSE_MKDIR(const char *path, mode_t mode) {
		WRITE_LOG("bb_mkdir(path=\"%s\", mode=0%3o)\n",
			path, mode);
		return FUSE_NEXT->fuse_mkdir(  path, mode);
	}
	FUSE_UNLINK(const char *path) {
		WRITE_LOG("bb_unlink(path=\"%s\")\n",
			path);
		return FUSE_NEXT->fuse_unlink(  path);
	}
	FUSE_RMDIR(const char *path) {
		WRITE_LOG("bb_rmdir(path=\"%s\")\n",
			path);
		return FUSE_NEXT->fuse_rmdir(  path);
	}
	FUSE_SYMLINK(const char *path, const char *link) {
		WRITE_LOG("bb_symlink(path=\"%s\", link=\"%s\")\n",
			path, link);
		return FUSE_NEXT->fuse_symlink(  path, link);
	}
	FUSE_RENAME(const char *path, const char *newpath) {
		WRITE_LOG("bb_rename(fpath=\"%s\", newpath=\"%s\")\n",
			path, newpath);
		return FUSE_NEXT->fuse_rename(  path, newpath);
	}
	FUSE_LINK(const char *path, const char *newpath) {
		WRITE_LOG("bb_link(path=\"%s\", newpath=\"%s\")\n",
			path, newpath);
		return FUSE_NEXT->fuse_link(  path, newpath);
	}
	FUSE_CHMOD(const char *path, mode_t mode) {
		WRITE_LOG("bb_chmod(fpath=\"%s\", mode=0%03o)\n",
			path, mode);
		return FUSE_NEXT->fuse_chmod(  path, mode);
	}
	FUSE_CHOWN(const char *path, uid_t uid, gid_t gid) {
		WRITE_LOG("bb_chown(path=\"%s\", uid=%d, gid=%d)\n",
			path, uid, gid);
		return FUSE_NEXT->fuse_chown(  path, uid, gid);
	}
	FUSE_TRUNCATE(const char *path, off_t newsize) {
		WRITE_LOG("bb_truncate(path=\"%s\", newsize=%lld)\n",
			path, newsize);
		return FUSE_NEXT->fuse_truncate(  path, newsize);
	}
	FUSE_UTIME(const char *path, struct utimbuf *ubuf) {
		WRITE_LOG("bb_utime(path=\"%s\", ubuf=0x%08x)\n",
			path, (unsigned int) ubuf);
		return FUSE_NEXT->fuse_utime(  path, ubuf);
	}
	FUSE_OPEN(const char *path, struct fuse_file_info *fi) {
		WRITE_LOG("bb_open(path\"%s\", fi=0x%08x)\n",
			path, (unsigned int) fi);
		return FUSE_NEXT->fuse_open(  path, fi);
	}
	FUSE_READ(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
		WRITE_LOG("bb_read(path=\"%s\", buf=0x%08x, size=%d, offset=%lld, fi=0x%08x)\n",
			path, (unsigned int) buf, size, offset, (unsigned int) fi);
		return FUSE_NEXT->fuse_read(  path, buf, size, offset, fi);
	}
	FUSE_WRITE(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
		WRITE_LOG("bb_write(path=\"%s\", buf=0x%08x, size=%d, offset=%lld, fi=0x%08x)\n",
			path, (unsigned int) buf, size, offset, (unsigned int) fi);
		return FUSE_NEXT->fuse_write(  path,  buf, size, offset, fi);
	}
	FUSE_STATFS(const char *path, struct statvfs *statv) {
		WRITE_LOG("bb_statfs(path=\"%s\", statv=0x%08x)\n",
			path, (unsigned int) statv);
		return FUSE_NEXT->fuse_statfs(  path, statv);
	}
	FUSE_FLUSH(const char *path, struct fuse_file_info *fi) {
		WRITE_LOG("bb_flush(path=\"%s\", fi=0x%08x)\n", path, (unsigned int) fi);
			// no need to get fpath on this one, since I work from fi->fh not the path
		return FUSE_NEXT->fuse_flush(  path, fi);
	}
	FUSE_RELEASE(const char *path, struct fuse_file_info *fi) {
		WRITE_LOG("bb_release(path=\"%s\", fi=0x%08x)\n",
			path, (unsigned int) fi);
		return FUSE_NEXT->fuse_release(  path, fi);
	}
	FUSE_FSYNC(const char *path, int datasync, struct fuse_file_info *fi) {
		WRITE_LOG("bb_fsync(path=\"%s\", datasync=%d, fi=0x%08x)\n",
			path, datasync, (unsigned int) fi);
		return FUSE_NEXT->fuse_fsync(  path, datasync, fi);
	}
	FUSE_SETXATTR(const char *path, const char *name, const char *value, size_t size, int flags) {
		WRITE_LOG("bb_setxattr(path=\"%s\", name=\"%s\", value=\"%s\", size=%d, flags=0x%08x)\n",
			path, name, value, size, flags);
		return FUSE_NEXT->fuse_setxattr(  path,  name,  value, size, flags);
	}
	FUSE_GETXATTR(const char *path, const char *name, char *value, size_t size) {
		WRITE_LOG("bb_getxattr(path = \"%s\", name = \"%s\", value = 0x%08x, size = %d)\n",
			path, name, (unsigned int) value, size);
		WRITE_LOG("    value = \"%s\"\n", value);
		return FUSE_NEXT->fuse_getxattr(  path,  name, value, size);
	}
			
	FUSE_LISTXATTR(const char *path, char *list, size_t size) {
		WRITE_LOG("bb_listxattr(path=\"%s\", list=0x%08x, size=%d)\n",
			path, (unsigned int) list, size);
		WRITE_LOG("    returned attributes (length %d):\n", retstat);
			for (ptr = list; ptr < list + retstat; ptr += strlen(ptr)+1)
		WRITE_LOG("    \"%s\"\n", ptr);
			
		return FUSE_NEXT->fuse_listxattr(  path, list, size);
	}
	FUSE_REMOVEXATTR(const char *path, const char *name) {
		WRITE_LOG("bb_removexattr(path=\"%s\", name=\"%s\")\n",
			path, name);
		return FUSE_NEXT->fuse_removexattr(  path,  name);
	}
	FUSE_OPENDIR(const char *path, struct fuse_file_info *fi) {
		WRITE_LOG("bb_opendir(path=\"%s\", fi=0x%08x)\n",
			path, (unsigned int) fi);
		return FUSE_NEXT->fuse_opendir(  path, fi);
	}
	FUSE_READDIR(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
		WRITE_LOG("bb_readdir(path=\"%s\", buf=0x%08x, filler=0x%08x, offset=%lld, fi=0x%08x)\n",
			path, (unsigned int) buf, (unsigned int) filler, offset, (unsigned int) fi);
		WRITE_LOG("calling filler with name %s\n", de->d_name);
		int result;
		if ((result = FUSE_NEXT->fuse_readdir(path, buf, filler, offset, fi, FUSE_NEXT_LAYER)) == -ENOMEM)
			WRITE_LOG("    ERROR bb_readdir filler:  buffer full");
		return result;
	}
	FUSE_RELEASEDIR(const char *path, struct fuse_file_info *fi) {
		WRITE_LOG("bb_releasedir(path=\"%s\", fi=0x%08x)\n",
			path, (unsigned int) fi);
		return FUSE_NEXT->fuse_releasedir(  path, fuse_file_info fi);
	}
	FUSE_FSYNCDIR(const char *path, int datasync, struct fuse_file_info *fi) {
		WRITE_LOG("bb_fsyncdir(path=\"%s\", datasync=%d, fi=0x%08x)\n",
			path, datasync, (unsigned int) fi);
		return FUSE_NEXT->fuse_fsyncdir(  path, datasync, fuse_file_info fi);
	}
	private:
	FILE *log;
};
